﻿// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2014/07/28 10:40
//
// License Copyright (c) Daniele Giardini.
// This work is subject to the terms at http://dotween.demigiant.com/license.php

#if COMPATIBLE
using DG.Tweening.Core.Surrogates;
using DOVector3 = DG.Tweening.Core.Surrogates.Vector3Wrapper;
using DOQuaternion = DG.Tweening.Core.Surrogates.QuaternionWrapper;
#else
using DOVector3 = UnityEngine.Vector3;
using DOQuaternion = UnityEngine.Quaternion;
#endif
using FT.Tweening.Core;
using FT.Tweening.Core.Enums;
using FT.Tweening.CustomPlugins;
using FT.Tweening.Plugins;
using FT.Tweening.Plugins.Core.PathCore;
using FT.Tweening.Plugins.Options;
using DOColor = UnityEngine.Color;
using UnityEngine;

#pragma warning disable 1573
namespace FT.Tweening
{
    /// <summary>
    /// Methods that extend known Unity objects and allow to directly create and control tweens from their instances
    /// </summary>
    public static class ShortcutExtensions
    {
        // ===================================================================================
        // CREATION SHORTCUTS ----------------------------------------------------------------

        #region Camera Shortcuts

        /// <summary>Tweens a Camera's <code>aspect</code> to the given value.
        /// Also stores the camera as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<float, float, FloatOptions> DOAspect(this Camera target, float endValue, int duration)
        {
            GuidTweenerCore<float, float, FloatOptions> t = FrameTween.To(() => target.aspect, x => target.aspect = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Camera's backgroundColor to the given value.
        /// Also stores the camera as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Color, Color, ColorOptions> DOColor(this Camera target, Color endValue, int duration)
        {
            GuidTweenerCore<Color, Color, ColorOptions> t = FrameTween.To(() => target.backgroundColor, x => target.backgroundColor = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Camera's <code>farClipPlane</code> to the given value.
        /// Also stores the camera as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<float, float, FloatOptions> DOFarClipPlane(this Camera target, float endValue, int duration)
        {
            GuidTweenerCore<float, float, FloatOptions> t = FrameTween.To(() => target.farClipPlane, x => target.farClipPlane = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Camera's <code>fieldOfView</code> to the given value.
        /// Also stores the camera as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<float, float, FloatOptions> DOFieldOfView(this Camera target, float endValue, int duration)
        {
            GuidTweenerCore<float, float, FloatOptions> t = FrameTween.To(() => target.fieldOfView, x => target.fieldOfView = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Camera's <code>nearClipPlane</code> to the given value.
        /// Also stores the camera as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<float, float, FloatOptions> DONearClipPlane(this Camera target, float endValue, int duration)
        {
            GuidTweenerCore<float, float, FloatOptions> t = FrameTween.To(() => target.nearClipPlane, x => target.nearClipPlane = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Camera's <code>orthographicSize</code> to the given value.
        /// Also stores the camera as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<float, float, FloatOptions> DOOrthoSize(this Camera target, float endValue, int duration)
        {
            GuidTweenerCore<float, float, FloatOptions> t = FrameTween.To(() => target.orthographicSize, x => target.orthographicSize = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Camera's <code>pixelRect</code> to the given value.
        /// Also stores the camera as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Rect, Rect, RectOptions> DOPixelRect(this Camera target, Rect endValue, int duration)
        {
            GuidTweenerCore<Rect, Rect, RectOptions> t = FrameTween.To(() => target.pixelRect, x => target.pixelRect = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Camera's <code>rect</code> to the given value.
        /// Also stores the camera as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Rect, Rect, RectOptions> DORect(this Camera target, Rect endValue, int duration)
        {
            GuidTweenerCore<Rect, Rect, RectOptions> t = FrameTween.To(() => target.rect, x => target.rect = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Shakes a Camera's localPosition along its relative X Y axes with the given values.
        /// Also stores the camera as the tween's target so it can be used for filtered operations</summary>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="strength">The shake strength</param>
        /// <param name="vibrato">Indicates how much will the shake vibrate</param>
        /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware). 
        /// Setting it to 0 will shake along a single direction.</param>
        /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
        /// <param name="randomnessMode">Randomness mode</param>
        public static GuidTweener DOShakePosition(this Camera target, int duration, float strength = 3, int vibrato = 10, float randomness = 90, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
        {
            if (duration <= 0)
            {
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOShakePosition: duration can't be 0, returning NULL without creating a tween");
                return null;
            }

            GuidTweener t = FrameTween.Shake(() => target.transform.localPosition,
                x => target.transform.localPosition = x, duration, strength, vibrato, randomness, true, fadeOut,
                randomnessMode, target.gameObject);
            t.Value.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetCameraShakePosition);
            return t;

        }
        /// <summary>Shakes a Camera's localPosition along its relative X Y axes with the given values.
        /// Also stores the camera as the tween's target so it can be used for filtered operations</summary>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="strength">The shake strength on each axis</param>
        /// <param name="vibrato">Indicates how much will the shake vibrate</param>
        /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware). 
        /// Setting it to 0 will shake along a single direction.</param>
        /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
        /// <param name="randomnessMode">Randomness mode</param>
        public static GuidTweener DOShakePosition(this Camera target, int duration, Vector3 strength, int vibrato = 10, float randomness = 90, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
        {
            if (duration <= 0)
            {
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOShakePosition: duration can't be 0, returning NULL without creating a tween");
                return null;
            }

            GuidTweener t = FrameTween.Shake(() => target.transform.localPosition,
                x => target.transform.localPosition = x, duration, strength, vibrato, randomness, fadeOut,
                randomnessMode, target.gameObject);
            t.Value.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetCameraShakePosition);
            return t;
        }

        /// <summary>Shakes a Camera's localRotation.
        /// Also stores the camera as the tween's target so it can be used for filtered operations</summary>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="strength">The shake strength</param>
        /// <param name="vibrato">Indicates how much will the shake vibrate</param>
        /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware). 
        /// Setting it to 0 will shake along a single direction.</param>
        /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
        /// <param name="randomnessMode">Randomness mode</param>
        public static GuidTweener DOShakeRotation(this Camera target, int duration, float strength = 90, int vibrato = 10, float randomness = 90, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
        {
            if (duration <= 0)
            {
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOShakeRotation: duration can't be 0, returning NULL without creating a tween");
                return null;
            }

            GuidTweener t = FrameTween.Shake(() => target.transform.localEulerAngles,
                x => target.transform.localRotation = Quaternion.Euler(x), duration, strength, vibrato, randomness,
                false, fadeOut, randomnessMode, target.gameObject);
            t.Value.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake);
            return t;

        }
        /// <summary>Shakes a Camera's localRotation.
        /// Also stores the camera as the tween's target so it can be used for filtered operations</summary>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="strength">The shake strength on each axis</param>
        /// <param name="vibrato">Indicates how much will the shake vibrate</param>
        /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware). 
        /// Setting it to 0 will shake along a single direction.</param>
        /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
        /// <param name="randomnessMode">Randomness mode</param>
        public static GuidTweener DOShakeRotation(this Camera target, int duration, Vector3 strength, int vibrato = 10, float randomness = 90, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
        {
            if (duration <= 0)
            {
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOShakeRotation: duration can't be 0, returning NULL without creating a tween");
                return null;
            }

            GuidTweener t = FrameTween.Shake(() => target.transform.localEulerAngles,
                x => target.transform.localRotation = Quaternion.Euler(x), duration, strength, vibrato, randomness,
                fadeOut, randomnessMode, target.gameObject);
            t.Value.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake);
            return t;

        }

        #endregion

        #region Light Shortcuts

        /// <summary>Tweens a Light's color to the given value.
        /// Also stores the light as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Color, Color, ColorOptions> DOColor(this Light target, Color endValue, int duration)
        {
            GuidTweenerCore<Color, Color, ColorOptions> t = FrameTween.To(() => target.color, x => target.color = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Light's intensity to the given value.
        /// Also stores the light as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<float, float, FloatOptions> DOIntensity(this Light target, float endValue, int duration)
        {
            GuidTweenerCore<float, float, FloatOptions> t = FrameTween.To(() => target.intensity, x => target.intensity = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Light's shadowStrength to the given value.
        /// Also stores the light as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<float, float, FloatOptions> DOShadowStrength(this Light target, float endValue, int duration)
        {
            GuidTweenerCore<float, float, FloatOptions> t = FrameTween.To(() => target.shadowStrength, x => target.shadowStrength = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        #endregion

        #region LineRenderer

        /// <summary>Tweens a LineRenderer's color to the given value.
        /// Also stores the LineRenderer as the tween's target so it can be used for filtered operations.
        /// <para>Note that this method requires to also insert the start colors for the tween, 
        /// since LineRenderers have no way to get them.</para></summary>
        /// <param name="startValue">The start value to tween from</param>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweener DOColor(this LineRenderer target, Color2 startValue, Color2 endValue, int duration)
        {
            GuidTweener t = FrameTween.To(() => startValue, x => target.SetColors(x.ca, x.cb), endValue, duration,
                target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        #endregion

        #region Renderer.Material Shortcuts

        /// <summary>Tweens a Material's color to the given value.
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Color, Color, ColorOptions> DOColor(this Material target, Color endValue, int duration)
        {
            GuidTweenerCore<Color, Color, ColorOptions> t = FrameTween.To(() => target.color, x => target.color = x, endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }
        /// <summary>Tweens a Material's named color property to the given value.
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="property">The name of the material property to tween (like _Tint or _SpecColor)</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Color, Color, ColorOptions> DOColor(this Material target, Color endValue, string property, int duration)
        {
            if (!target.HasProperty(property))
            {
                if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(property);
                return null;
            }
            GuidTweenerCore<Color, Color, ColorOptions> t = FrameTween.To(() => target.GetColor(property), x => target.SetColor(property, x), endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }
        /// <summary>Tweens a Material's named color property with the given ID to the given value.
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="propertyID">The ID of the material property to tween (also called nameID in Unity's manual)</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Color, Color, ColorOptions> DOColor(this Material target, Color endValue, int propertyID, int duration)
        {
            if (!target.HasProperty(propertyID))
            {
                if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(propertyID);
                return null;
            }
            GuidTweenerCore<Color, Color, ColorOptions> t = FrameTween.To(() => target.GetColor(propertyID), x => target.SetColor(propertyID, x), endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Material's alpha color to the given value
        /// (will have no effect unless your material supports transparency).
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Color, Color, ColorOptions> DOFade(this Material target, float endValue, int duration)
        {
            GuidTweenerCore<Color, Color, ColorOptions> t = FrameTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }
        /// <summary>Tweens a Material's alpha color to the given value
        /// (will have no effect unless your material supports transparency).
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="property">The name of the material property to tween (like _Tint or _SpecColor)</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Color, Color, ColorOptions> DOFade(this Material target, float endValue, string property, int duration)
        {
            if (!target.HasProperty(property))
            {
                if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(property);
                return null;
            }
            GuidTweenerCore<Color, Color, ColorOptions> t = FrameTween.ToAlpha(() => target.GetColor(property), x => target.SetColor(property, x), endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }
        /// <summary>Tweens a Material's alpha color with the given ID to the given value
        /// (will have no effect unless your material supports transparency).
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="propertyID">The ID of the material property to tween (also called nameID in Unity's manual)</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Color, Color, ColorOptions> DOFade(this Material target, float endValue, int propertyID, int duration)
        {
            if (!target.HasProperty(propertyID))
            {
                if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(propertyID);
                return null;
            }
            GuidTweenerCore<Color, Color, ColorOptions> t = FrameTween.ToAlpha(() => target.GetColor(propertyID), x => target.SetColor(propertyID, x), endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Material's named float property to the given value.
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="property">The name of the material property to tween</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<float, float, FloatOptions> DOFloat(this Material target, float endValue, string property, int duration)
        {
            if (!target.HasProperty(property))
            {
                if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(property);
                return null;
            }
            GuidTweenerCore<float, float, FloatOptions> t = FrameTween.To(() => target.GetFloat(property), x => target.SetFloat(property, x), endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }
        /// <summary>Tweens a Material's named float property with the given ID to the given value.
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="propertyID">The ID of the material property to tween (also called nameID in Unity's manual)</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<float, float, FloatOptions> DOFloat(this Material target, float endValue, int propertyID, int duration)
        {
            if (!target.HasProperty(propertyID))
            {
                if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(propertyID);
                return null;
            }
            GuidTweenerCore<float, float, FloatOptions> t = FrameTween.To(() => target.GetFloat(propertyID), x => target.SetFloat(propertyID, x), endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Material's texture offset to the given value.
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Vector2, Vector2, VectorOptions> DOOffset(this Material target, Vector2 endValue, int duration)
        {
            GuidTweenerCore<Vector2, Vector2, VectorOptions> t = FrameTween.To(() => target.mainTextureOffset, x => target.mainTextureOffset = x, endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }
        /// <summary>Tweens a Material's named texture offset property to the given value.
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="property">The name of the material property to tween</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Vector2, Vector2, VectorOptions> DOOffset(this Material target, Vector2 endValue, string property, int duration)
        {
            if (!target.HasProperty(property))
            {
                if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(property);
                return null;
            }
            GuidTweenerCore<Vector2, Vector2, VectorOptions> t = FrameTween.To(() => target.GetTextureOffset(property), x => target.SetTextureOffset(property, x), endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Material's texture scale to the given value.
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Vector2, Vector2, VectorOptions> DOTiling(this Material target, Vector2 endValue, int duration)
        {
            GuidTweenerCore<Vector2, Vector2, VectorOptions> t = FrameTween.To(() => target.mainTextureScale, x => target.mainTextureScale = x, endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }
        /// <summary>Tweens a Material's named texture scale property to the given value.
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="property">The name of the material property to tween</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Vector2, Vector2, VectorOptions> DOTiling(this Material target, Vector2 endValue, string property, int duration)
        {
            if (!target.HasProperty(property))
            {
                if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(property);
                return null;
            }
            GuidTweenerCore<Vector2, Vector2, VectorOptions> t = FrameTween.To(() => target.GetTextureScale(property), x => target.SetTextureScale(property, x), endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Material's named Vector property to the given value.
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="property">The name of the material property to tween</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Vector4, Vector4, VectorOptions> DOVector(this Material target, Vector4 endValue, string property, int duration)
        {
            if (!target.HasProperty(property))
            {
                if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(property);
                return null;
            }
            GuidTweenerCore<Vector4, Vector4, VectorOptions> t = FrameTween.To(() => target.GetVector(property), x => target.SetVector(property, x), endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }
        /// <summary>Tweens a Material's named Vector property with the given ID to the given value.
        /// Also stores the material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="propertyID">The ID of the material property to tween (also called nameID in Unity's manual)</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Vector4, Vector4, VectorOptions> DOVector(this Material target, Vector4 endValue, int propertyID, int duration)
        {
            if (!target.HasProperty(propertyID))
            {
                if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(propertyID);
                return null;
            }
            GuidTweenerCore<Vector4, Vector4, VectorOptions> t = FrameTween.To(() => target.GetVector(propertyID), x => target.SetVector(propertyID, x), endValue, duration, null);
            t.Value.SetTarget(target);
            return t;
        }

        #endregion

        #region TrailRenderer Shortcuts

        /// <summary>Tweens a TrailRenderer's startWidth/endWidth to the given value.
        /// Also stores the TrailRenderer as the tween's target so it can be used for filtered operations</summary>
        /// <param name="toStartWidth">The end startWidth to reach</param><param name="toEndWidth">The end endWidth to reach</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweener DOResize(this TrailRenderer target, float toStartWidth, float toEndWidth, int duration)
        {
            GuidTweener t = FrameTween.To(() => new Vector2(target.startWidth, target.endWidth), x =>
            {
#if COMPATIBLE
                    target.startWidth = x.value.x;
                    target.endWidth = x.value.y;
#else
                target.startWidth = x.x;
                target.endWidth = x.y;
#endif
            }, new Vector2(toStartWidth, toEndWidth), duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;

        }

        /// <summary>Tweens a TrailRenderer's time to the given value.
        /// Also stores the TrailRenderer as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<float, float, FloatOptions> DOTime(this TrailRenderer target, float endValue, int duration)
        {
            GuidTweenerCore<float, float, FloatOptions> t = FrameTween.To(() => target.time, x => target.time = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        #endregion

        #region Transform Shortcuts

        /// <summary>Tweens a Transform's position to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        public static GuidTweenerCore<Vector3, Vector3, VectorOptions> DOMove(this Transform target, Vector3 endValue, int duration, bool snapping = false)
        {
            GuidTweenerCore<Vector3, Vector3, VectorOptions> t = FrameTween.To(() => target.position, x => target.position = x, endValue, duration, target.gameObject);
            t.Value.SetOptions(snapping).SetTarget(target);
            return t;
        }
        public static GuidTweenerCore<Vector3, Vector3, VectorOptions> DOMove(this Transform target, Vector3 endValue, float duration, bool snapping = false)
        {
            int frameDuration =(int)(duration * FrameTween.Rate);
            GuidTweenerCore<Vector3, Vector3, VectorOptions> t = FrameTween.To(() => target.position, x => target.position = x, endValue, frameDuration, target.gameObject);
            t.Value.SetOptions(snapping).SetTarget(target);
            t.Value.updateType = UpdateType.Time;
            return t;
        }
        /// <summary>Tweens a Transform's X position to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        public static GuidTweenerCore<DOVector3, DOVector3, VectorOptions> DOMoveX(this Transform target, float endValue, int duration, bool snapping = false)
        {
            GuidTweenerCore<DOVector3, DOVector3, VectorOptions> t = FrameTween.To(() => target.position, x => target.position = x, new Vector3(endValue, 0, 0), duration, target.gameObject);
            t.Value.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's Y position to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        public static GuidTweenerCore<Vector3, Vector3, VectorOptions> DOMoveY(this Transform target, float endValue, int duration, bool snapping = false)
        {
            GuidTweenerCore<Vector3, Vector3, VectorOptions> t = FrameTween.To(() => target.position, x => target.position = x, new Vector3(0, endValue, 0), duration, target.gameObject);
            t.Value.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's Z position to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        public static GuidTweenerCore<Vector3, Vector3, VectorOptions> DOMoveZ(this Transform target, float endValue, int duration, bool snapping = false)
        {
            GuidTweenerCore<Vector3, Vector3, VectorOptions> t = FrameTween.To(() => target.position, x => target.position = x, new Vector3(0, 0, endValue), duration, target.gameObject);
            t.Value.SetOptions(AxisConstraint.Z, snapping).SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's localPosition to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        public static GuidTweenerCore<Vector3, Vector3, VectorOptions> DOLocalMove(this Transform target, Vector3 endValue, int duration, bool snapping = false)
        {
            GuidTweenerCore<Vector3, Vector3, VectorOptions> t = FrameTween.To(() => target.localPosition, x => target.localPosition = x, endValue, duration, target.gameObject);
            t.Value.SetOptions(snapping).SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's X localPosition to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        public static GuidTweenerCore<Vector3, Vector3, VectorOptions> DOLocalMoveX(this Transform target, float endValue, int duration, bool snapping = false)
        {
            GuidTweenerCore<Vector3, Vector3, VectorOptions> t = FrameTween.To(() => target.localPosition, x => target.localPosition = x, new Vector3(endValue, 0, 0), duration, target.gameObject);
            t.Value.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's Y localPosition to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        public static GuidTweenerCore<Vector3, Vector3, VectorOptions> DOLocalMoveY(this Transform target, float endValue, int duration, bool snapping = false)
        {
            GuidTweenerCore<Vector3, Vector3, VectorOptions> t = FrameTween.To(() => target.localPosition, x => target.localPosition = x, new Vector3(0, endValue, 0), duration, target.gameObject);
            t.Value.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's Z localPosition to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        public static GuidTweenerCore<Vector3, Vector3, VectorOptions> DOLocalMoveZ(this Transform target, float endValue, int duration, bool snapping = false)
        {
            GuidTweenerCore<Vector3, Vector3, VectorOptions> t = FrameTween.To(() => target.localPosition, x => target.localPosition = x, new Vector3(0, 0, endValue), duration, target.gameObject);
            t.Value.SetOptions(AxisConstraint.Z, snapping).SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's rotation to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        /// <param name="mode">Rotation mode</param>
        public static GuidTweenerCore<DOQuaternion, DOVector3, QuaternionOptions> DORotate(this Transform target, Vector3 endValue, int duration, RotateMode mode = RotateMode.Fast)
        {
            GuidTweenerCore<DOQuaternion, DOVector3, QuaternionOptions> t = FrameTween.To(() => target.rotation, x => target.rotation = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            t.Value.plugOptions.rotateMode = mode;
            return t;
        }

        /// <summary>Tweens a Transform's rotation to the given value using pure quaternion values.
        /// Also stores the transform as the tween's target so it can be used for filtered operations.
        /// <para>PLEASE NOTE: DORotate, which takes Vector3 values, is the preferred rotation method.
        /// This method was implemented for very special cases, and doesn't support LoopType.Incremental loops
        /// (neither for itself nor if placed inside a LoopType.Incremental Sequence)</para>
        /// </summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<DOQuaternion, DOQuaternion, NoOptions> DORotateQuaternion(this Transform target, Quaternion endValue, int duration)
        {
            GuidTweenerCore<DOQuaternion, DOQuaternion, NoOptions> t = FrameTween.To(PureQuaternionPlugin.Plug(), () => target.rotation, x => target.rotation = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's localRotation to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        /// <param name="mode">Rotation mode</param>
        public static GuidTweenerCore<DOQuaternion, DOVector3, QuaternionOptions> DOLocalRotate(this Transform target, Vector3 endValue, int duration, RotateMode mode = RotateMode.Fast)
        {
            GuidTweenerCore<DOQuaternion, DOVector3, QuaternionOptions> t = FrameTween.To(() => target.localRotation, x => target.localRotation = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            t.Value.plugOptions.rotateMode = mode;
            return t;
        }

        /// <summary>Tweens a Transform's rotation to the given value using pure quaternion values.
        /// Also stores the transform as the tween's target so it can be used for filtered operations.
        /// <para>PLEASE NOTE: DOLocalRotate, which takes Vector3 values, is the preferred rotation method.
        /// This method was implemented for very special cases, and doesn't support LoopType.Incremental loops
        /// (neither for itself nor if placed inside a LoopType.Incremental Sequence)</para>
        /// </summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Quaternion, Quaternion, NoOptions> DOLocalRotateQuaternion(this Transform target, Quaternion endValue, int duration)
        {
            GuidTweenerCore<Quaternion, Quaternion, NoOptions> t = FrameTween.To(PureQuaternionPlugin.Plug(), () => target.localRotation, x => target.localRotation = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's localScale to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Vector3, Vector3, VectorOptions> DOScale(this Transform target, Vector3 endValue, int duration)
        {
            GuidTweenerCore<Vector3, Vector3, VectorOptions> t = FrameTween.To(() => target.localScale, x => target.localScale = x, endValue, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's localScale uniformly to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Vector3, Vector3, VectorOptions> DOScale(this Transform target, float endValue, int duration)
        {
            Vector3 endValueV3 = new Vector3(endValue, endValue, endValue);
            GuidTweenerCore<Vector3, Vector3, VectorOptions> t = FrameTween.To(() => target.localScale, x => target.localScale = x, endValueV3, duration, target.gameObject);
            t.Value.SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's X localScale to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Vector3, Vector3, VectorOptions> DOScaleX(this Transform target, float endValue, int duration)
        {
            GuidTweenerCore<Vector3, Vector3, VectorOptions> t = FrameTween.To(() => target.localScale, x => target.localScale = x, new Vector3(endValue, 0, 0), duration, target.gameObject);
            t.Value.SetOptions(AxisConstraint.X)
                .SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's Y localScale to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Vector3, Vector3, VectorOptions> DOScaleY(this Transform target, float endValue, int duration)
        {
            GuidTweenerCore<Vector3, Vector3, VectorOptions> t = FrameTween.To(() => target.localScale, x => target.localScale = x, new Vector3(0, endValue, 0), duration, target.gameObject);
            t.Value.SetOptions(AxisConstraint.Y)
                .SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's Z localScale to the given value.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
        public static GuidTweenerCore<Vector3, Vector3, VectorOptions> DOScaleZ(this Transform target, float endValue, int duration)
        {
            GuidTweenerCore<Vector3, Vector3, VectorOptions> t = FrameTween.To(() => target.localScale, x => target.localScale = x, new Vector3(0, 0, endValue), duration, target.gameObject);
            t.Value.SetOptions(AxisConstraint.Z)
                .SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's rotation so that it will look towards the given world position.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="towards">The position to look at</param><param name="duration">The duration of the tween</param>
        /// <param name="axisConstraint">Eventual axis constraint for the rotation</param>
        /// <param name="up">The vector that defines in which direction up is (default: Vector3.up)</param>
        public static GuidTweener DOLookAt(this Transform target, Vector3 towards, int duration, AxisConstraint axisConstraint = AxisConstraint.None, Vector3? up = null)
        { return LookAt(target, towards, duration, axisConstraint, up, false); }
        /// <summary><code>EXPERIMENTAL</code> Tweens a Transform's rotation so that it will look towards the given world position,
        /// while also updating the lookAt position every frame
        /// (contrary to <see cref="DOLookAt"/> which calculates the lookAt rotation only once, when the tween starts).
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="towards">The position to look at</param><param name="duration">The duration of the tween</param>
        /// <param name="axisConstraint">Eventual axis constraint for the rotation</param>
        /// <param name="up">The vector that defines in which direction up is (default: Vector3.up)</param>
        public static GuidTweener DODynamicLookAt(this Transform target, Vector3 towards, int duration, AxisConstraint axisConstraint = AxisConstraint.None, Vector3? up = null)
        { return LookAt(target, towards, duration, axisConstraint, up, true); }
        static GuidTweener LookAt(this Transform target, Vector3 towards, int duration, AxisConstraint axisConstraint, Vector3? up, bool dynamic)
        {
            GuidTweenerCore<DOQuaternion, DOVector3, QuaternionOptions> t = FrameTween.To(() => target.rotation, x => target.rotation = x, towards, duration);
            t.Value.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetLookAt);
            t.Value.plugOptions.axisConstraint = axisConstraint;
            t.Value.plugOptions.up = (up == null) ? Vector3.up : (Vector3)up;
            if (dynamic)
            {
                t.Value.plugOptions.dynamicLookAt = true;
                t.Value.plugOptions.dynamicLookAtWorldPosition = towards;
            }
            else t.Value.plugOptions.dynamicLookAt = false;
            return t;
        }

        /// <summary>Punches a Transform's localPosition towards the given direction and then back to the starting one
        /// as if it was connected to the starting position via an elastic.</summary>
        /// <param name="punch">The direction and strength of the punch (added to the Transform's current position)</param>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="vibrato">Indicates how much will the punch vibrate</param>
        /// <param name="elasticity">Represents how much (0 to 1) the vector will go beyond the starting position when bouncing backwards.
        /// 1 creates a full oscillation between the punch direction and the opposite direction,
        /// while 0 oscillates only between the punch and the start position</param>
        /// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
        public static GuidTweener DOPunchPosition(this Transform target, Vector3 punch, int duration, int vibrato = 10, float elasticity = 1, bool snapping = false)
        {
            if (duration <= 0)
            {
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOPunchPosition: duration can't be 0, returning NULL without creating a tween");
                return null;
            }

            GuidTweenerCore<Vector3, Vector3[], Vector3ArrayOptions> t = FrameTween.Punch(() => target.localPosition, x => target.localPosition = x, punch, duration,
                vibrato, elasticity, target.gameObject);
            t.Value.SetTarget(target).SetOptions(snapping);
            return t;

        }
        /// <summary>Punches a Transform's localScale towards the given size and then back to the starting one
        /// as if it was connected to the starting scale via an elastic.</summary>
        /// <param name="punch">The punch strength (added to the Transform's current scale)</param>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="vibrato">Indicates how much will the punch vibrate</param>
        /// <param name="elasticity">Represents how much (0 to 1) the vector will go beyond the starting size when bouncing backwards.
        /// 1 creates a full oscillation between the punch scale and the opposite scale,
        /// while 0 oscillates only between the punch scale and the start scale</param>
        public static GuidTweener DOPunchScale(this Transform target, Vector3 punch, int duration, int vibrato = 10, float elasticity = 1)
        {
            if (duration <= 0)
            {
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOPunchScale: duration can't be 0, returning NULL without creating a tween");
                return null;
            }

            GuidTweener t = FrameTween.Punch(() => target.localScale, x => target.localScale = x, punch, duration, vibrato, elasticity,
                target.gameObject);
            t.Value.SetTarget(target);
            return t;

        }
        /// <summary>Punches a Transform's localRotation towards the given size and then back to the starting one
        /// as if it was connected to the starting rotation via an elastic.</summary>
        /// <param name="punch">The punch strength (added to the Transform's current rotation)</param>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="vibrato">Indicates how much will the punch vibrate</param>
        /// <param name="elasticity">Represents how much (0 to 1) the vector will go beyond the starting rotation when bouncing backwards.
        /// 1 creates a full oscillation between the punch rotation and the opposite rotation,
        /// while 0 oscillates only between the punch and the start rotation</param>
        public static GuidTweener DOPunchRotation(this Transform target, Vector3 punch, int duration, int vibrato = 10, float elasticity = 1)
        {
            if (duration <= 0)
            {
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOPunchRotation: duration can't be 0, returning NULL without creating a tween");
                return null;
            }

            GuidTweener t = FrameTween.Punch(() => target.localEulerAngles,
                x => target.localRotation = Quaternion.Euler(x), punch, duration, vibrato, elasticity,
                target.gameObject);
            t.Value.SetTarget(target);
            return t;

        }

        /// <summary>Shakes a Transform's localPosition with the given values.</summary>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="strength">The shake strength</param>
        /// <param name="vibrato">Indicates how much will the shake vibrate</param>
        /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware). 
        /// Setting it to 0 will shake along a single direction.</param>
        /// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
        /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
        /// <param name="randomnessMode">Randomness mode</param>
        public static GuidTweener DOShakePosition(this Transform target, int duration, float strength = 1, int vibrato = 10, float randomness = 90, bool snapping = false, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
        {
            if (duration <= 0)
            {
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOShakePosition: duration can't be 0, returning NULL without creating a tween");
                return null;
            }
            GuidTweenerCore<Vector3, Vector3[], Vector3ArrayOptions> t = FrameTween.Shake(() => target.localPosition, x => target.localPosition = x, duration, strength,
                vibrato, randomness, false, fadeOut, randomnessMode, target.gameObject);
            t.Value.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake).SetOptions(snapping);
            return t;

        }
        /// <summary>Shakes a Transform's localPosition with the given values.</summary>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="strength">The shake strength on each axis</param>
        /// <param name="vibrato">Indicates how much will the shake vibrate</param>
        /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware). 
        /// Setting it to 0 will shake along a single direction.</param>
        /// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
        /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
        /// <param name="randomnessMode">Randomness mode</param>
        public static GuidTweener DOShakePosition(this Transform target, int duration, Vector3 strength, int vibrato = 10, float randomness = 90, bool snapping = false, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
        {
            if (duration <= 0)
            {
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOShakePosition: duration can't be 0, returning NULL without creating a tween");
                return null;
            }

            GuidTweenerCore<Vector3, Vector3[], Vector3ArrayOptions> t = FrameTween.Shake(() => target.localPosition, x => target.localPosition = x, duration,
                strength, vibrato, randomness, fadeOut, randomnessMode, target.gameObject);
            t.Value.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake).SetOptions(snapping);
            return t;

        }
        /// <summary>Shakes a Transform's localRotation.</summary>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="strength">The shake strength</param>
        /// <param name="vibrato">Indicates how much will the shake vibrate</param>
        /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware). 
        /// Setting it to 0 will shake along a single direction.</param>
        /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
        /// <param name="randomnessMode">Randomness mode</param>
        public static GuidTweener DOShakeRotation(this Transform target, int duration, float strength = 90, int vibrato = 10, float randomness = 90, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
        {
            if (duration <= 0)
            {
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOShakeRotation: duration can't be 0, returning NULL without creating a tween");
                return null;
            }

            GuidTweenerCore<Vector3, Vector3[], Vector3ArrayOptions> t = FrameTween.Shake(() => target.localEulerAngles, x => target.localRotation = Quaternion.Euler(x), duration,
                strength, vibrato, randomness, false, fadeOut, randomnessMode, target.gameObject);
            t.Value.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake);
            return t;

        }
        /// <summary>Shakes a Transform's localRotation.</summary>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="strength">The shake strength on each axis</param>
        /// <param name="vibrato">Indicates how much will the shake vibrate</param>
        /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware). 
        /// Setting it to 0 will shake along a single direction.</param>
        /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
        /// <param name="randomnessMode">Randomness mode</param>
        public static GuidTweener DOShakeRotation(this Transform target, int duration, Vector3 strength, int vibrato = 10, float randomness = 90, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
        {
            if (duration <= 0)
            {
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOShakeRotation: duration can't be 0, returning NULL without creating a tween");
                return null;
            }

            GuidTweenerCore<Vector3, Vector3[], Vector3ArrayOptions> t = FrameTween.Shake(() => target.localEulerAngles, x => target.localRotation = Quaternion.Euler(x), duration,
                strength, vibrato, randomness, fadeOut, randomnessMode, target.gameObject);
            t.Value.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake);
            return t;

        }
        /// <summary>Shakes a Transform's localScale.</summary>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="strength">The shake strength</param>
        /// <param name="vibrato">Indicates how much will the shake vibrate</param>
        /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware). 
        /// Setting it to 0 will shake along a single direction.</param>
        /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
        /// <param name="randomnessMode">Randomness mode</param>
        public static GuidTweener DOShakeScale(this Transform target, int duration, float strength = 1, int vibrato = 10, float randomness = 90, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
        {
            if (duration <= 0)
            {
                Debugger.Log(Debugger.logPriority);
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOShakeScale: duration can't be 0, returning NULL without creating a tween");
                return null;
            }

            GuidTweenerCore<Vector3, Vector3[], Vector3ArrayOptions> t = FrameTween.Shake(() => target.localScale, x => target.localScale = x, duration, strength, vibrato,
                randomness, false, fadeOut, randomnessMode, target.gameObject);
            t.Value.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake);
            return t;

        }
        /// <summary>Shakes a Transform's localScale.</summary>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="strength">The shake strength on each axis</param>
        /// <param name="vibrato">Indicates how much will the shake vibrate</param>
        /// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware). 
        /// Setting it to 0 will shake along a single direction.</param>
        /// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
        /// <param name="randomnessMode">Randomness mode</param>
        public static GuidTweener DOShakeScale(this Transform target, int duration, Vector3 strength, int vibrato = 10, float randomness = 90, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
        {
            if (duration <= 0)
            {
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOShakeScale: duration can't be 0, returning NULL without creating a tween");
                return null;
            }

            GuidTweenerCore<Vector3, Vector3[], Vector3ArrayOptions> t = FrameTween.Shake(() => target.localScale,
                x => target.localScale = x, duration, strength, vibrato, randomness, fadeOut, randomnessMode,
                target.gameObject);
            t.Value.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake);
            return t;

        }

        #region Special

        /// <summary>Tweens a Transform's position to the given value, while also applying a jump effect along the Y axis.
        /// Returns a Sequence instead of a Tweener.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="jumpPower">Power of the jump (the max height of the jump is represented by this plus the final Y offset)</param>
        /// <param name="numJumps">Total number of jumps</param>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
        public static GuidSequence DOJump(this Transform target, Vector3 endValue, float jumpPower, int numJumps, int duration, bool snapping = false)
        {
            if (numJumps < 1) numJumps = 1;
            float startPosY = target.position.y; // Temporary fix for OnStart not being called when using Goto instead of GotoWithCallbacks
            float offsetY = -1;
            bool offsetYSet = false;

            // Separate Y Tween so we can elaborate elapsedPercentage on that instead of on the Sequence
            // (in case users add a delay or other elements to the Sequence)
            GuidSequence s = FrameTween.Sequence();
            GuidTweenerCore<DOVector3, DOVector3, VectorOptions> yTween = FrameTween.To(() => target.position, x => target.position = x,
                new Vector3(0, jumpPower, 0), duration / (numJumps * 2), target.gameObject);
            yTween.Value.SetOptions(AxisConstraint.Y, snapping).SetEase(Ease.OutQuad).SetRelative()
                .SetLoops(numJumps * 2, LoopType.Yoyo)
                .OnStart(() => startPosY = target.position.y); // FIXME not called if you only use Goto (and not GotoWithCallbacks)
            GuidTweenerCore<DOVector3, DOVector3, VectorOptions> t = FrameTween.To(() => target.position,
                x => target.position = x, new Vector3(endValue.x, 0, 0), duration, target.gameObject);
            GuidTweenerCore<DOVector3, DOVector3, VectorOptions> t2 = FrameTween.To(() => target.position,
                x => target.position = x, new Vector3(0, 0, endValue.z), duration, target.gameObject);
            t.Value.SetOptions(AxisConstraint.X, snapping).SetEase(Ease.Linear);
            t2.Value.SetOptions(AxisConstraint.Z, snapping).SetEase(Ease.Linear);
            s.Value.Append(
                    t.Value
                ).Join(
                    t2.Value
                ).Join(yTween.Value)
                .SetTarget(target).SetEase(FrameTween.defaultEaseType);
            yTween.Value.OnUpdate(() =>
            {
                if (!offsetYSet)
                {
                    offsetYSet = true;
                    offsetY = s.Value.isRelative ? endValue.y : endValue.y - startPosY;
                }
                Vector3 pos = target.position;
                pos.y += FrameVirtual.EasedValue(0, offsetY, yTween.Value.ElapsedPercentage(), Ease.OutQuad);
                target.position = pos;
            });
            return s;

            // Old incorrect method
            //            Sequence s = DOTween.Sequence();
            //            s.Append(DOTween.To(() => target.position, x => target.position = x, new Vector3(endValue.x, 0, 0), duration)
            //                    .SetOptions(AxisConstraint.X, snapping).SetEase(Ease.Linear)
            //                ).Join(DOTween.To(() => target.position, x => target.position = x, new Vector3(0, 0, endValue.z), duration)
            //                    .SetOptions(AxisConstraint.Z, snapping).SetEase(Ease.Linear)
            //                ).Join(DOTween.To(() => target.position, x => target.position = x, new Vector3(0, jumpPower, 0), duration / (numJumps * 2))
            //                    .SetOptions(AxisConstraint.Y, snapping).SetEase(Ease.OutQuad).SetRelative()
            //                    .SetLoops(numJumps * 2, LoopType.Yoyo)
            //                ).SetTarget(target).SetEase(DOTween.defaultEaseType)
            //                .OnUpdate(() => {
            //                    if (!offsetYSet) {
            //                        offsetYSet = true;
            //                        offsetY = s.isRelative ? endValue.y : endValue.y - startPosY;
            //                    }
            //                    Vector3 pos = target.position;
            //                    Debug.Log(offsetY + " > " + s.ElapsedDirectionalPercentage());
            //                    pos.y += DOVirtual.EasedValue(0, offsetY, s.ElapsedDirectionalPercentage(), Ease.OutQuad);
            //                    target.position = pos;
            //                });
            //            return s;
        }
        /// <summary>Tweens a Transform's localPosition to the given value, while also applying a jump effect along the Y axis.
        /// Returns a Sequence instead of a Tweener.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The end value to reach</param>
        /// <param name="jumpPower">Power of the jump (the max height of the jump is represented by this plus the final Y offset)</param>
        /// <param name="numJumps">Total number of jumps</param>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
        public static GuidSequence DOLocalJump(this Transform target, Vector3 endValue, float jumpPower, int numJumps, int duration, bool snapping = false)
        {
            if (numJumps < 1) numJumps = 1;
            float startPosY = target.localPosition.y; // Temporary fix for OnStart not being called when using Goto instead of GotoWithCallbacks
            float offsetY = -1;
            bool offsetYSet = false;

            // Separate Y Tween so we can elaborate elapsedPercentage on that instead of on the Sequence
            // (in case users add a delay or other elements to the Sequence)
            GuidSequence s = FrameTween.Sequence();
            GuidTweenerCore<DOVector3, DOVector3, VectorOptions> yTween = FrameTween.To(() => target.localPosition, x => target.localPosition = x, new Vector3(0, jumpPower, 0), duration / (numJumps * 2), target.gameObject);
            yTween.Value
                .SetOptions(AxisConstraint.Y, snapping).SetEase(Ease.OutQuad).SetRelative()
                .SetLoops(numJumps * 2, LoopType.Yoyo)
                .OnStart(() => startPosY = target.localPosition.y); // FIXME not called if you only use Goto (and not GotoWithCallbacks)

            GuidTweenerCore<DOVector3, DOVector3, VectorOptions> t = FrameTween.To(() => target.localPosition,
                x => target.localPosition = x, new Vector3(endValue.x, 0, 0), duration, target.gameObject);

            GuidTweenerCore<DOVector3, DOVector3, VectorOptions> t2 = FrameTween.To(() => target.localPosition,
                x => target.localPosition = x, new Vector3(0, 0, endValue.z), duration, target.gameObject);
            t.Value.SetOptions(AxisConstraint.X, snapping).SetEase(Ease.Linear);
            t2.Value.SetOptions(AxisConstraint.Z, snapping).SetEase(Ease.Linear);
            s.Value.Append(
                    t.Value
                ).Join(t2.Value)
                 .Join(yTween.Value)
                .SetTarget(target).SetEase(FrameTween.defaultEaseType);
            yTween.Value.OnUpdate(() =>
            {
                if (!offsetYSet)
                {
                    offsetYSet = true;
                    offsetY = s.Value.isRelative ? endValue.y : endValue.y - startPosY;
                }
                Vector3 pos = target.localPosition;
                pos.y += FrameVirtual.EasedValue(0, offsetY, yTween.Value.ElapsedPercentage(), Ease.OutQuad);
                target.localPosition = pos;
            });
            return s;

            // Old incorrect method
            // if (numJumps < 1) numJumps = 1;
            // float startPosY = target.localPosition.y;
            // float offsetY = -1;
            // bool offsetYSet = false;
            // Sequence s = DOTween.Sequence();
            // s.Append(DOTween.To(() => target.localPosition, x => target.localPosition = x, new Vector3(endValue.x, 0, 0), duration)
            //         .SetOptions(AxisConstraint.X, snapping).SetEase(Ease.Linear)
            //     ).Join(DOTween.To(() => target.localPosition, x => target.localPosition = x, new Vector3(0, 0, endValue.z), duration)
            //         .SetOptions(AxisConstraint.Z, snapping).SetEase(Ease.Linear)
            //     ).Join(DOTween.To(() => target.localPosition, x => target.localPosition = x, new Vector3(0, jumpPower, 0), duration / (numJumps * 2))
            //         .SetOptions(AxisConstraint.Y, snapping).SetEase(Ease.OutQuad).SetRelative()
            //         .SetLoops(numJumps * 2, LoopType.Yoyo)
            //     ).SetTarget(target).SetEase(DOTween.defaultEaseType)
            //     .OnUpdate(() => {
            //         if (!offsetYSet) {
            //             offsetYSet = false;
            //             offsetY = s.isRelative ? endValue.y : endValue.y - startPosY;
            //         }
            //         Vector3 pos = target.localPosition;
            //         pos.y += DOVirtual.EasedValue(0, offsetY, s.ElapsedDirectionalPercentage(), Ease.OutQuad);
            //         target.localPosition = pos;
            //     });
            // return s;
        }

        /// <summary>Tweens a Transform's position through the given path waypoints, using the chosen path algorithm.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="path">The waypoints to go through</param>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="pathType">The type of path: Linear (straight path), CatmullRom (curved CatmullRom path) or CubicBezier (curved with control points)</param>
        /// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
        /// <param name="resolution">The resolution of the path (useless in case of Linear paths): higher resolutions make for more detailed curved paths but are more expensive.
        /// Defaults to 10, but a value of 5 is usually enough if you don't have dramatic long curves between waypoints</param>
        /// <param name="gizmoColor">The color of the path (shown when gizmos are active in the Play panel and the tween is running)</param>
        public static GuidTweenerCore<Vector3, Path, PathOptions> DOPath(
            this Transform target, Vector3[] path, int duration, PathType pathType = PathType.Linear,
            PathMode pathMode = PathMode.Full3D, int resolution = 10, Color? gizmoColor = null
        )
        {
            if (resolution < 1) resolution = 1;
            GuidTweenerCore<Vector3, Path, PathOptions> t = FrameTween.To(PathPlugin.Get(), () => target.position, x => target.position = x, new Path(pathType, path, resolution, gizmoColor), duration, target.gameObject);
                
            t.Value.SetTarget(target);

            t.Value.plugOptions.mode = pathMode;
            return t;
        }
        /// <summary>Tweens a Transform's localPosition through the given path waypoints, using the chosen path algorithm.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="path">The waypoint to go through</param>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="pathType">The type of path: Linear (straight path), CatmullRom (curved CatmullRom path) or CubicBezier (curved with control points)</param>
        /// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
        /// <param name="resolution">The resolution of the path: higher resolutions make for more detailed curved paths but are more expensive.
        /// Defaults to 10, but a value of 5 is usually enough if you don't have dramatic long curves between waypoints</param>
        /// <param name="gizmoColor">The color of the path (shown when gizmos are active in the Play panel and the tween is running)</param>
        public static GuidTweenerCore<Vector3, Path, PathOptions> DOLocalPath(
            this Transform target, Vector3[] path, int duration, PathType pathType = PathType.Linear,
            PathMode pathMode = PathMode.Full3D, int resolution = 10, Color? gizmoColor = null
        )
        {
            if (resolution < 1) resolution = 1;
            GuidTweenerCore<Vector3, Path, PathOptions> t = FrameTween.To(PathPlugin.Get(), () => target.localPosition, x => target.localPosition = x, new Path(pathType, path, resolution, gizmoColor), duration, target.gameObject);
            t.Value.SetTarget(target);

            t.Value.plugOptions.mode = pathMode;
            t.Value.plugOptions.useLocalPosition = true;
            return t;
        }
        // Used by path editor when creating the actual tween, so it can pass a pre-compiled path
        /// <summary>IMPORTANT: Unless you really know what you're doing, you should use the overload that accepts a Vector3 array instead.<para/>
        /// Tweens a Transform's position via the given path.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="path">The path to use</param>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
        public static GuidTweenerCore<Vector3, Path, PathOptions> DOPath(
            this Transform target, Path path, int duration, PathMode pathMode = PathMode.Full3D
        )
        {
            GuidTweenerCore<Vector3, Path, PathOptions> t = FrameTween.To(PathPlugin.Get(), () => target.position, x => target.position = x, path, duration, target.gameObject);
            t.Value.SetTarget(target);

            t.Value.plugOptions.mode = pathMode;
            return t;
        }
        // Used by path editor when creating the actual tween, so it can pass a pre-compiled path
        /// <summary>IMPORTANT: Unless you really know what you're doing, you should use the overload that accepts a Vector3 array instead.<para/>
        /// Tweens a Transform's localPosition via the given path.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="path">The path to use</param>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
        public static GuidTweenerCore<Vector3, Path, PathOptions> DOLocalPath(
            this Transform target, Path path, int duration, PathMode pathMode = PathMode.Full3D
        )
        {
            GuidTweenerCore<Vector3, Path, PathOptions> t = FrameTween.To(PathPlugin.Get(), () => target.localPosition, x => target.localPosition = x, path, duration, target.gameObject);
            t.Value.SetTarget(target);

            t.Value.plugOptions.mode = pathMode;
            t.Value.plugOptions.useLocalPosition = true;
            return t;
        }

        #endregion

        #endregion

        #region Blendables

        #region Light

        /// <summary>Tweens a Light's color to the given value,
        /// in a way that allows other DOBlendableColor tweens to work together on the same target,
        /// instead than fight each other as multiple DOColor would do.
        /// Also stores the Light as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
        public static GuidTweener DOBlendableColor(this Light target, Color endValue, int duration)
        {
            endValue = endValue - target.color;
            Color to = new Color(0, 0, 0, 0);
            GuidTweenerCore<DOColor, DOColor, ColorOptions> t = FrameTween.To(() => to, x =>
            {
#if COMPATIBLE
                Color diff = x.value - to;
#else
                Color diff = x - to;
#endif
                to = x;
                target.color += diff;
            }, endValue, duration, target.gameObject);
            t.Value.Blendable().SetTarget(target);
            return t;
        }

        #endregion

        #region Renderer.Material

        /// <summary>Tweens a Material's color to the given value,
        /// in a way that allows other DOBlendableColor tweens to work together on the same target,
        /// instead than fight each other as multiple DOColor would do.
        /// Also stores the Material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
        public static GuidTweener DOBlendableColor(this Material target, Color endValue, int duration)
        {
            endValue = endValue - target.color;
            Color to = new Color(0, 0, 0, 0);
            GuidTweenerCore<DOColor, DOColor, ColorOptions> t = FrameTween.To(() => to, x =>
            {
#if COMPATIBLE
                Color diff = x.value - to;
#else
                Color diff = x - to;
#endif
                to = x;
                target.color += diff;
            }, endValue, duration, null); 
            t.Value.Blendable().SetTarget(target);
            return t;

        }
        /// <summary>Tweens a Material's named color property to the given value,
        /// in a way that allows other DOBlendableColor tweens to work together on the same target,
        /// instead than fight each other as multiple DOColor would do.
        /// Also stores the Material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The value to tween to</param>
        /// <param name="property">The name of the material property to tween (like _Tint or _SpecColor)</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweener DOBlendableColor(this Material target, Color endValue, string property, int duration)
        {
            if (!target.HasProperty(property))
            {
                if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(property);
                return null;
            }

            endValue = endValue - target.GetColor(property);
            Color to = new Color(0, 0, 0, 0);
            GuidTweenerCore<DOColor, DOColor, ColorOptions> t = FrameTween.To(() => to, x =>
            {
#if COMPATIBLE
                Color diff = x.value - to;
#else
                Color diff = x - to;
#endif
                to = x;
                target.SetColor(property, target.GetColor(property) + diff);
            }, endValue, duration, null);
            t.Value.Blendable().SetTarget(target);
            return t;

        }
        /// <summary>Tweens a Material's named color property with the given ID to the given value,
        /// in a way that allows other DOBlendableColor tweens to work together on the same target,
        /// instead than fight each other as multiple DOColor would do.
        /// Also stores the Material as the tween's target so it can be used for filtered operations</summary>
        /// <param name="endValue">The value to tween to</param>
        /// <param name="propertyID">The ID of the material property to tween (also called nameID in Unity's manual)</param>
        /// <param name="duration">The duration of the tween</param>
        public static GuidTweener DOBlendableColor(this Material target, Color endValue, int propertyID, int duration)
        {
            if (!target.HasProperty(propertyID))
            {
                if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(propertyID);
                return null;
            }

            endValue = endValue - target.GetColor(propertyID);
            Color to = new Color(0, 0, 0, 0);
            GuidTweenerCore<DOColor, DOColor, ColorOptions> t = FrameTween.To(() => to, x =>
            {
#if COMPATIBLE
                Color diff = x.value - to;
#else
                Color diff = x - to;
#endif
                to = x;
                target.SetColor(propertyID, target.GetColor(propertyID) + diff);
            }, endValue, duration, null);
            t.Value.Blendable().SetTarget(target);
            return t;
        }

        #endregion

        #region Transform

        /// <summary>Tweens a Transform's position BY the given value (as if you chained a <code>SetRelative</code>),
        /// in a way that allows other DOBlendableMove tweens to work together on the same target,
        /// instead than fight each other as multiple DOMove would do.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="byValue">The value to tween by</param><param name="duration">The duration of the tween</param>
        /// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
        public static GuidTweener DOBlendableMoveBy(this Transform target, Vector3 byValue, int duration, bool snapping = false)
        {
            Vector3 to = Vector3.zero;
            GuidTweenerCore<DOVector3, DOVector3, VectorOptions> t = FrameTween.To(() => to, x =>
                {
#if COMPATIBLE
                Vector3 diff = x.value - to;
#else
                    Vector3 diff = x - to;
#endif
                    to = x;
                    target.position += diff;
                }, byValue, duration
                , target.gameObject);
            t.Value.Blendable().SetOptions(snapping).SetTarget(target);
            return t;
        }

        /// <summary>Tweens a Transform's localPosition BY the given value (as if you chained a <code>SetRelative</code>),
        /// in a way that allows other DOBlendableMove tweens to work together on the same target,
        /// instead than fight each other as multiple DOMove would do.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="byValue">The value to tween by</param><param name="duration">The duration of the tween</param>
        /// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
        public static GuidTweener DOBlendableLocalMoveBy(this Transform target, Vector3 byValue, int duration, bool snapping = false)
        {
            Vector3 to = Vector3.zero;
            GuidTweenerCore<DOVector3, DOVector3, VectorOptions> t = FrameTween.To(() => to, x =>
                {
#if COMPATIBLE
                Vector3 diff = x.value - to;
#else
                    Vector3 diff = x - to;
#endif
                    to = x;
                    target.localPosition += diff;
                }, byValue, duration
                , target.gameObject);
            t.Value.Blendable().SetOptions(snapping).SetTarget(target);
            return t;

        }

        /// <summary>EXPERIMENTAL METHOD - Tweens a Transform's rotation BY the given value (as if you chained a <code>SetRelative</code>),
        /// in a way that allows other DOBlendableRotate tweens to work together on the same target,
        /// instead than fight each other as multiple DORotate would do.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="byValue">The value to tween by</param><param name="duration">The duration of the tween</param>
        /// <param name="mode">Rotation mode</param>
        public static GuidTweener DOBlendableRotateBy(this Transform target, Vector3 byValue, int duration, RotateMode mode = RotateMode.Fast)
        {
            //            Quaternion to = target.rotation;
            Quaternion to = Quaternion.identity;
            GuidTweenerCore<DOQuaternion, DOVector3, QuaternionOptions> t = FrameTween.To(() => to, x =>
            {
#if COMPATIBLE
                Quaternion diff = x.value * Quaternion.Inverse(to);
#else
                Quaternion diff = x * Quaternion.Inverse(to);
#endif
                to = x;
                Quaternion currRot = target.rotation;
                target.rotation = currRot * Quaternion.Inverse(currRot) * diff * currRot;
            }, byValue, duration, target.gameObject);
            t.Value.Blendable().SetTarget(target);
            t.Value.plugOptions.rotateMode = mode;
            return t;
        }

        /// <summary>EXPERIMENTAL METHOD - Tweens a Transform's lcoalRotation BY the given value (as if you chained a <code>SetRelative</code>),
        /// in a way that allows other DOBlendableRotate tweens to work together on the same target,
        /// instead than fight each other as multiple DORotate would do.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="byValue">The value to tween by</param><param name="duration">The duration of the tween</param>
        /// <param name="mode">Rotation mode</param>
        public static GuidTweener DOBlendableLocalRotateBy(this Transform target, Vector3 byValue, int duration, RotateMode mode = RotateMode.Fast)
        {
            //            Quaternion to = target.localRotation;
            Quaternion to = Quaternion.identity;
            GuidTweenerCore<DOQuaternion, DOVector3, QuaternionOptions> t = FrameTween.To(() => to, x =>
            {
#if COMPATIBLE
                Quaternion diff = x.value * Quaternion.Inverse(to);
#else
                Quaternion diff = x * Quaternion.Inverse(to);
#endif
                to = x;
                Quaternion currRot = target.localRotation;
                target.localRotation = currRot * Quaternion.Inverse(currRot) * diff * currRot;
            }, byValue, duration, target.gameObject);
            t.Value.Blendable().SetTarget(target);
            t.Value.plugOptions.rotateMode = mode;
            return t;
        }

#if !COMPATIBLE
        // Added by Steve Streeting > https://github.com/sinbad
        /// <summary>Punches a Transform's localRotation BY the given value and then back to the starting one
        /// as if it was connected to the starting rotation via an elastic. Does it in a way that allows other
        /// DOBlendableRotate tweens to work together on the same target</summary>
        /// <param name="punch">The punch strength (added to the Transform's current rotation)</param>
        /// <param name="duration">The duration of the tween</param>
        /// <param name="vibrato">Indicates how much will the punch vibrate</param>
        /// <param name="elasticity">Represents how much (0 to 1) the vector will go beyond the starting rotation when bouncing backwards.
        /// 1 creates a full oscillation between the punch rotation and the opposite rotation,
        /// while 0 oscillates only between the punch and the start rotation</param>
        public static GuidTweener DOBlendablePunchRotation(this Transform target, Vector3 punch, int duration, int vibrato = 10, float elasticity = 1)
        {
            if (duration <= 0)
            {
                if (Debugger.logPriority > 0) Debugger.LogWarning("DOBlendablePunchRotation: duration can't be 0, returning NULL without creating a tween");
                return null;
            }
            Vector3 to = Vector3.zero;
            GuidTweenerCore<DOVector3, DOVector3[], Vector3ArrayOptions> t = FrameTween.Punch(() => to, v =>
            {
                Quaternion qto = Quaternion.Euler(to.x, to.y, to.z);
                Quaternion qnew = Quaternion.Euler(v.x, v.y, v.z);
#if COMPATIBLE
                Quaternion diff = x.value * Quaternion.Inverse(qto);
#else
                Quaternion diff = qnew * Quaternion.Inverse(qto);
#endif
                to = v;
                Quaternion currRot = target.rotation;
                target.rotation = currRot * Quaternion.Inverse(currRot) * diff * currRot;
            }, punch, duration, vibrato, elasticity, target.gameObject);
                t.Value.Blendable().SetTarget(target);
            return t;
        }
#endif

        /// <summary>Tweens a Transform's localScale BY the given value (as if you chained a <code>SetRelative</code>),
        /// in a way that allows other DOBlendableScale tweens to work together on the same target,
        /// instead than fight each other as multiple DOScale would do.
        /// Also stores the transform as the tween's target so it can be used for filtered operations</summary>
        /// <param name="byValue">The value to tween by</param><param name="duration">The duration of the tween</param>
        public static GuidTweener DOBlendableScaleBy(this Transform target, Vector3 byValue, int duration)
        {
            Vector3 to = Vector3.zero;
            GuidTweenerCore<DOVector3, DOVector3, VectorOptions> t = FrameTween.To(() => to, x =>
                {
#if COMPATIBLE
                Vector3 diff = x.value - to;
#else
                    Vector3 diff = x - to;
#endif
                    to = x;
                    target.localScale += diff;
                }, byValue, duration
                , target.gameObject);
            t.Value.Blendable().SetTarget(target);
            return t;

        }

        #endregion

        #endregion

        // ===================================================================================
        // OPERATION SHORTCUTS ---------------------------------------------------------------

        #region Operation Shortcuts

        /// <summary>
        /// Completes all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens completed
        /// (meaning the tweens that don't have infinite loops and were not already complete)
        /// </summary>
        /// <param name="withCallbacks">For Sequences only: if TRUE also internal Sequence callbacks will be fired,
        /// otherwise they will be ignored</param>
        public static int DOComplete(this Component target, bool withCallbacks = false)
        {
            return FrameTween.Complete(target, withCallbacks);
        }
        /// <summary>
        /// Completes all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens completed
        /// (meaning the tweens that don't have infinite loops and were not already complete)
        /// </summary>
        /// <param name="withCallbacks">For Sequences only: if TRUE also internal Sequence callbacks will be fired,
        /// otherwise they will be ignored</param>
        public static int DOComplete(this Material target, bool withCallbacks = false)
        {
            return FrameTween.Complete(target, withCallbacks);
        }

        /// <summary>
        /// Kills all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens killed.
        /// </summary>
        /// <param name="complete">If TRUE completes the tween before killing it</param>
        public static int DOKill(this Component target, bool complete = false)
        {
            //            int tot = complete ? DOTween.CompleteAndReturnKilledTot(target) : 0;
            //            return tot + DOTween.Kill(target);
            return FrameTween.Kill(target, complete);
        }
        /// <summary>
        /// Kills all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens killed.
        /// </summary>
        /// <param name="complete">If TRUE completes the tween before killing it</param>
        public static int DOKill(this Material target, bool complete = false)
        {
            return FrameTween.Kill(target, complete);
        }

        /// <summary>
        /// Flips the direction (backwards if it was going forward or viceversa) of all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens flipped.
        /// </summary>
        public static int DOFlip(this Component target)
        {
            return FrameTween.Flip(target);
        }
        /// <summary>
        /// Flips the direction (backwards if it was going forward or viceversa) of all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens flipped.
        /// </summary>
        public static int DOFlip(this Material target)
        {
            return FrameTween.Flip(target);
        }

        /// <summary>
        /// Sends to the given position all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens involved.
        /// </summary>
        /// <param name="to">Time position to reach
        /// (if higher than the whole tween duration the tween will simply reach its end)</param>
        /// <param name="andPlay">If TRUE will play the tween after reaching the given position, otherwise it will pause it</param>
        public static int DOGoto(this Component target, int to, bool andPlay = false)
        {
            return FrameTween.Goto(target, to, andPlay);
        }
        /// <summary>
        /// Sends to the given position all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens involved.
        /// </summary>
        /// <param name="to">Time position to reach
        /// (if higher than the whole tween duration the tween will simply reach its end)</param>
        /// <param name="andPlay">If TRUE will play the tween after reaching the given position, otherwise it will pause it</param>
        public static int DOGoto(this Material target, int to, bool andPlay = false)
        {
            return FrameTween.Goto(target, to, andPlay);
        }

        /// <summary>
        /// Pauses all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens paused.
        /// </summary>
        public static int DOPause(this Component target)
        {
            return FrameTween.Pause(target);
        }
        /// <summary>
        /// Pauses all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens paused.
        /// </summary>
        public static int DOPause(this Material target)
        {
            return FrameTween.Pause(target);
        }

        /// <summary>
        /// Plays all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens played.
        /// </summary>
        public static int DOPlay(this Component target)
        {
            return FrameTween.Play(target);
        }
        /// <summary>
        /// Plays all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens played.
        /// </summary>
        public static int DOPlay(this Material target)
        {
            return FrameTween.Play(target);
        }

        /// <summary>
        /// Plays backwards all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens played.
        /// </summary>
        public static int DOPlayBackwards(this Component target)
        {
            return FrameTween.PlayBackwards(target);
        }
        /// <summary>
        /// Plays backwards all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens played.
        /// </summary>
        public static int DOPlayBackwards(this Material target)
        {
            return FrameTween.PlayBackwards(target);
        }

        /// <summary>
        /// Plays forward all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens played.
        /// </summary>
        public static int DOPlayForward(this Component target)
        {
            return FrameTween.PlayForward(target);
        }
        /// <summary>
        /// Plays forward all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens played.
        /// </summary>
        public static int DOPlayForward(this Material target)
        {
            return FrameTween.PlayForward(target);
        }

        /// <summary>
        /// Restarts all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens restarted.
        /// </summary>
        public static int DORestart(this Component target, bool includeDelay = true)
        {
            return FrameTween.Restart(target, includeDelay);
        }
        /// <summary>
        /// Restarts all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens restarted.
        /// </summary>
        public static int DORestart(this Material target, bool includeDelay = true)
        {
            return FrameTween.Restart(target, includeDelay);
        }

        /// <summary>
        /// Rewinds all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens rewinded.
        /// </summary>
        public static int DORewind(this Component target, bool includeDelay = true)
        {
            return FrameTween.Rewind(target, includeDelay);
        }
        /// <summary>
        /// Rewinds all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens rewinded.
        /// </summary>
        public static int DORewind(this Material target, bool includeDelay = true)
        {
            return FrameTween.Rewind(target, includeDelay);
        }

        /// <summary>
        /// Smoothly rewinds all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens rewinded.
        /// </summary>
        public static int DOSmoothRewind(this Component target)
        {
            return FrameTween.SmoothRewind(target);
        }
        /// <summary>
        /// Smoothly rewinds all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens rewinded.
        /// </summary>
        public static int DOSmoothRewind(this Material target)
        {
            return FrameTween.SmoothRewind(target);
        }

        /// <summary>
        /// Toggles the paused state (plays if it was paused, pauses if it was playing) of all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens involved.
        /// </summary>
        public static int DOTogglePause(this Component target)
        {
            return FrameTween.TogglePause(target);
        }
        /// <summary>
        /// Toggles the paused state (plays if it was paused, pauses if it was playing) of all tweens that have this target as a reference
        /// (meaning tweens that were started from this target, or that had this target added as an Id)
        /// and returns the total number of tweens involved.
        /// </summary>
        public static int DOTogglePause(this Material target)
        {
            return FrameTween.TogglePause(target);
        }

        #endregion
    }
}